home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Graphics / NXPlot3d / Source / PControl.m < prev    next >
Text File  |  1994-02-18  |  16KB  |  610 lines

  1. #import "PControl.h"
  2. #import "Plot3DView.h"
  3. #import "PlotShape.h"
  4. #import <appkit/appkit.h>
  5. #import <dpsclient/event.h>
  6. #import <dpsclient/psops.h>
  7. #import <appkit/color.h>
  8. #import <stdio.h>
  9. #import <math.h>
  10. #import <ctype.h>
  11. #include <libc.h>
  12. #import "Expression.h"
  13. #import "DensView.h"
  14.  
  15. int comp(float *x,float *y);
  16.  
  17. @implementation PControl
  18. -init
  19. {
  20. [super init];
  21. return self;
  22. }
  23.  
  24. /* Called once by the Plot3DView to do initialization and pass pref pointer */
  25. -startup:(SetPref *)Pref
  26. {
  27.     pref=Pref;
  28. /* display initial preferences settings */
  29.     curPref=0;
  30.     bpath=(char *)[[NXBundle mainBundle] directory];
  31.     [symsel selectCellWithTag:pref[0].sym+1];
  32.     equation=[equation docView];
  33.     [equation setText:[pref[0].expr text]];
  34. /* make sure plot agrees with displayed min/max values */
  35.     [gridx setIntValue:pref[curPref].nx];
  36.     [gridy setIntValue:pref[curPref].ny];
  37.     [d3View zoomTo:[minX floatValue] :[minY floatValue] :[maxX floatValue] :[maxY floatValue]];
  38.     [d3View setDelegate:self];
  39. /*[self readFile:self];*/
  40.  
  41.     I_lim=[I_lim contentView];
  42.     I_dis=[I_dis contentView];
  43.     I_pho=[I_pho contentView];
  44. return self;
  45. }
  46.  
  47. /* display preferences for a data set */
  48. -disPref:sender
  49. {
  50. int i;
  51. curPref=[[sender selectedCell] tag];
  52. [symsel selectCellWithTag:pref[curPref].sym+1];
  53. [color1 setColor:NXConvertRGBToColor(pref[curPref].mapcol[0][0], 
  54.     pref[curPref].mapcol[0][1],pref[curPref].mapcol[0][2])];
  55. [color2 setColor:NXConvertRGBToColor(pref[curPref].mapcol[1][0], 
  56.     pref[curPref].mapcol[1][1],pref[curPref].mapcol[1][2])];
  57. [color3 setColor:NXConvertRGBToColor(pref[curPref].mapcol[2][0], 
  58.     pref[curPref].mapcol[2][1],pref[curPref].mapcol[2][2])];
  59. [color4 setColor:NXConvertRGBToColor(pref[curPref].mapcol[3][0], 
  60.     pref[curPref].mapcol[3][1],pref[curPref].mapcol[3][2])];
  61. [color5 setColor:NXConvertRGBToColor(pref[curPref].mapcol[4][0], 
  62.     pref[curPref].mapcol[4][1],pref[curPref].mapcol[4][2])];
  63.  
  64. [colortype selectCellWithTag:pref[curPref].mapmode];
  65.  
  66. for (i=0; i<5; i++) 
  67.     [[colorsel cellAt:0 :i] setIntValue:pref[curPref].mapsel[i]];
  68.  
  69. if (pref[curPref].fileData==NULL) [ffSel selectCellWithTag:0];
  70. else [ffSel selectCellWithTag:1];
  71. [equation setText:[pref[curPref].expr text]];
  72. [gridx setIntValue:pref[curPref].nx];
  73. [gridy setIntValue:pref[curPref].ny];
  74. [d3View zoom:self];
  75. return self;
  76. }
  77.  
  78. /* put preferences from screen into SetPref for curPref set */
  79. -stoPref:sender
  80. {
  81. char *tmp;
  82. int i,j,k;
  83. pref[curPref].sym=[[symsel selectedCell] tag]-1;
  84. pref[curPref].nx=[gridx intValue];
  85. if (pref[curPref].nx>MAXGRID||pref[curPref].nx<4) 
  86.     [gridx setIntValue:pref[curPref].nx=10];
  87. pref[curPref].ny=[gridy intValue];
  88. if (pref[curPref].ny>MAXGRID||pref[curPref].ny<4) 
  89.     [gridy setIntValue:pref[curPref].ny=10];
  90. tmp=malloc([equation textLength]+50);
  91. [equation getSubstring:tmp start:0 length:[equation textLength]+1];
  92. /* Fix equation for parsing */
  93. k=strlen(tmp);
  94. for (i=0; i<k; i++) {
  95.     if (tmp[i]=='[') tmp[i]='(';
  96.     if (tmp[i]==']') tmp[i]=')';
  97.     if (isupper(tmp[i])) tmp[i]=tolower(tmp[i]);
  98.     if ((tmp[i]=='+'||tmp[i]=='-')&&(isdigit(tmp[i+1])||tmp[i+1]=='.')) {
  99.         for (j=k; j>i; j--) tmp[j+1]=tmp[j];
  100.         tmp[i+1]=' ';
  101.         k++;
  102.     }
  103. }
  104.  
  105. /* if the equation doesn't parse, set it to 0 */
  106. if (strlen(tmp)==0 ||
  107.      [pref[curPref].expr parse:tmp]==0) {
  108.     [self error:"Cannot parse equation!"];
  109.     [pref[curPref].expr parse:"0"];
  110.     }
  111.  
  112. /* Colors */
  113. NXConvertColorToRGB([color1 color],&pref[curPref].mapcol[0][0], 
  114.     &pref[curPref].mapcol[0][1],&pref[curPref].mapcol[0][2]);
  115. NXConvertColorToRGB([color2 color],&pref[curPref].mapcol[1][0], 
  116.     &pref[curPref].mapcol[1][1],&pref[curPref].mapcol[1][2]);
  117. NXConvertColorToRGB([color3 color],&pref[curPref].mapcol[2][0], 
  118.     &pref[curPref].mapcol[2][1],&pref[curPref].mapcol[2][2]);
  119. NXConvertColorToRGB([color4 color],&pref[curPref].mapcol[3][0], 
  120.     &pref[curPref].mapcol[3][1],&pref[curPref].mapcol[3][2]);
  121. NXConvertColorToRGB([color5 color],&pref[curPref].mapcol[4][0], 
  122.     &pref[curPref].mapcol[4][1],&pref[curPref].mapcol[4][2]);
  123.  
  124. for (i=0; i<5; i++) pref[curPref].mapsel[i]=[[colorsel cellAt:0 :i] intValue];
  125.     
  126. pref[curPref].mapmode=[[colortype selectedCell] tag];
  127.  
  128. /* if spherical plot, set proper limits */
  129. if (pref[curPref].sym==6) {
  130.     [minX setFloatValue:0];
  131.     [maxX setFloatValue:M_PI];
  132.     [minY setFloatValue:0];
  133.     [maxY setFloatValue:M_PI*2.0];
  134.     [self setMinMax:self];
  135. }
  136. [d3View zoom:self];
  137. return self;
  138. }
  139.  
  140. /* Read 3d data from a file and put it into the current data set */
  141. /* does NOT wait for an "OK" press, like other prefs */
  142. -readFile:sender
  143. {
  144. id panel;
  145. int i,j,k,cc=0,nx,ny;
  146. char s[201],sc[20];
  147. FILE *in;
  148. float x0,y0,z0,z1,f,x[MAXGRID+1],y[MAXGRID+1];
  149. Point *p;
  150.  
  151. if (pref[curPref].fileData!=NULL) { free(pref[curPref].fileData); pref[curPref].
  152. fileData=NULL; }
  153.  
  154. if (sender!=self) {
  155.     /* get a filespec from the user */
  156.     panel=[[OpenPanel new] allowMultipleFiles:NO];
  157.     if (![panel runModalForTypes:NULL]) {
  158.         [ffSel selectCellWithTag:0];
  159.         [equation setText:"0"];
  160.         [pref[curPref].expr parse:"0"];
  161.         return self;
  162.     }
  163.     
  164.     /* if we can't open the file, go back to equation mode */
  165.     in=fopen([panel filename],"r");
  166. }
  167. else {
  168.     [ffSel selectCellWithTag:1];
  169.     sprintf(s,"%s/title.3d",bpath);
  170.     in=fopen(s,"r");
  171. }
  172.  
  173. if (fgets(s,200,in)==NULL) {
  174.     [ffSel selectCellWithTag:0];
  175.     [equation setText:"0"];
  176.     [pref[curPref].expr parse:"0"];
  177.     [self error:"Can't open file."];
  178.     return self;
  179. }
  180.  
  181. /* ignore leading # lines */
  182. while (s[0]=='#') { fgets(s,80,in); cc++; }
  183.  
  184. /* accept either "x,y,z\n" or "x y z\n" files */
  185. strcpy(sc," %f , %f , %f");
  186. if (sscanf(s,sc,&x[0],&y[0],&z0)!=3) {
  187.     strcpy(sc," %f %f %f");
  188.     if (sscanf(s,sc,&x[0],&y[0],&z0)!=3) {
  189.         [ffSel selectCellWithTag:0];
  190.         [equation setText:"0"];
  191.         [pref[curPref].expr parse:"0"];
  192.         [self error:"File not in 'x y z' or 'x,y,z' format."];
  193.         return self;
  194.     }
  195. }
  196. z1=z0;
  197.  
  198. /* count the number of points in the file */
  199. nx=ny=1;
  200. while (fscanf(in,sc,&x[nx],&y[ny],&f)==3) {
  201.     if (f<z0) z0=f;
  202.     for (i=0; i<nx; i++) if (x[nx]==x[i]) break;
  203.     if (i==nx) nx++;
  204.     for (i=0; i<ny; i++) if (y[ny]==y[i]) break;
  205.     if (i==ny) ny++;
  206.     if (nx==MAXGRID+1||ny==MAXGRID+1) {
  207.         [ffSel selectCellWithTag:0];
  208.         [equation setText:"0"];
  209.         [pref[curPref].expr parse:"0"];
  210.         [self error:"More than MAXGRID points in x or y."];
  211.         fclose(in);
  212.          return self;
  213.     }
  214. }
  215. qsort(x,nx,sizeof(float),comp);    
  216. qsort(y,ny,sizeof(float),comp);    
  217. rewind(in);
  218.  
  219. /* 2nd pass, allocate memory,initialize and read data into array */
  220. p=pref[curPref].fileData=malloc(sizeof(Point)*nx*ny);
  221. pref[curPref].nfdata=nx*ny;
  222. for (i=0; i<nx; i++) {
  223.     for (j=0; j<ny; j++) {
  224.         p[i+nx*j].x=x[i];
  225.         p[i+nx*j].y=y[j];
  226.         p[i+nx*j].z=z0;
  227.     }
  228. }
  229.  
  230. for (i=0; i<cc; i++) fgets(s,240,in);
  231. while (fscanf(in,sc,&x0,&y0,&f)==3) {
  232.     for (j=0; j<nx; j++) if (x0==x[j]) break;
  233.     for (k=0; k<ny; k++) if (y0==y[k]) break;
  234.     if (k==ny||j==nx) [self error:"Unusual error, notify author."];
  235.     p[j+nx*k].z=f;
  236. }
  237. if (pref[curPref].data!=NULL) free(pref[curPref].data);
  238. pref[curPref].data=malloc(sizeof(Point)*pref[curPref].nfdata);
  239. pref[curPref].color=malloc(sizeof(RtColor)*pref[curPref].nfdata);
  240. [minX setFloatValue:x[0]];
  241. [minY setFloatValue:y[0]];
  242. [maxX setFloatValue:x[nx-1]];
  243. [maxY setFloatValue:y[ny-1]];
  244.  
  245. /* equation is now used to modify data Z values, set initial formula for */
  246. /* z(x,y,z)=z. ie - for log plots you would enter "log(z)" */
  247. [equation setText:"z"];
  248. [pref[curPref].expr parse:"z"];
  249.  
  250. /* update the display */
  251. [self setMinMax:self];
  252. [d3View zoom:self];
  253. return self;
  254. }
  255.  
  256. /* return a data set to equation mode, free memory used by the file */
  257. -clearFile:sender
  258. {
  259. if (pref[curPref].fileData!=NULL) { free(pref[curPref].fileData); pref[curPref].fileData=NULL; }
  260. pref[curPref].nfdata=0;
  261.  
  262. [equation setText:"0"];
  263. [pref[curPref].expr parse:"0"];
  264. [d3View zoom:self];
  265. return(self);
  266. }
  267.  
  268. /* minz and maxz can be modified before returning, but currently aren't */
  269. -minmaxZ:(float *)minz :(float *)maxz
  270. {
  271. [dminZ setFloatValue:*minz];
  272. [dmaxZ setFloatValue:*maxz];
  273. if ([dzSwitch intValue]) {
  274.     [minZ setFloatValue:*minz];
  275.     [maxZ setFloatValue:*maxz];
  276. }
  277. *minz=[minZ floatValue];
  278. *maxz=[maxZ floatValue];
  279. return self;
  280. }
  281.  
  282. /* display new min/max values (does not inform the views of the change) */
  283. -setMM:(float)minx :(float)maxx :(float)miny :(float)maxy
  284. {
  285. [minX setFloatValue:minx];
  286. [minY setFloatValue:miny];
  287. [maxX setFloatValue:maxx];
  288. [maxY setFloatValue:maxy];
  289. return self;
  290. }
  291.  
  292. /* Tell the Plot3DView and (indirectly) the DensView about new max/min vals */
  293. -setMinMax:sender
  294. {
  295. [d3View zoomTo:[minX floatValue] :[minY floatValue] :[maxX floatValue] :[maxY floatValue]];
  296. [d3View zoom:self];
  297. return self;
  298. }
  299.  
  300. /* zoom in by a fixed amount */
  301. -zoomIn:sender
  302. {
  303. float x,y,x1,y1;
  304. x1=[maxX floatValue];
  305. x=[minX floatValue];
  306. y1=[maxY floatValue];
  307. y=[minY floatValue];
  308.  
  309. [maxX setFloatValue:x1-(x1-x)/4.0];
  310. [minX setFloatValue:x+(x1-x)/4.0];
  311. [maxY setFloatValue:y1-(y1-y)/4.0];
  312. [minY setFloatValue:y+(y1-y)/4.0];
  313.  
  314. [self setMinMax:self];
  315. [d3View zoom:self];
  316. return self;
  317. }
  318.  
  319. /* zoom out by a fixed amount */
  320. -zoomOut:sender
  321. {
  322. float x,y,x1,y1;
  323. x1=[maxX floatValue];
  324. x=[minX floatValue];
  325. y1=[maxY floatValue];
  326. y=[minY floatValue];
  327.  
  328. [maxX setFloatValue:x1+(x1-x)/4.0];
  329. [minX setFloatValue:x-(x1-x)/4.0];
  330. [maxY setFloatValue:y1+(y1-y)/4.0];
  331. [minY setFloatValue:y-(y1-y)/4.0];
  332.  
  333. [self setMinMax:self];
  334. [d3View zoom:self];
  335. return self;
  336. }
  337.  
  338. /* sent by DensView, min/max are from 0.0 to 1.0, not real units */
  339. -zoomTo:(float)minx :(float)miny :(float)maxx :(float)maxy
  340. {
  341. float x0,x1,y0,y1;
  342.  
  343. x0=[minX floatValue];
  344. x1=[maxX floatValue];
  345. y0=[minY floatValue];
  346. y1=[maxY floatValue];
  347.  
  348. [minX setFloatValue:x0+(x1-x0)*minx];
  349. [minY setFloatValue:y0+(y1-y0)*miny];
  350. [maxX setFloatValue:x0+(x1-x0)*maxx];
  351. [maxY setFloatValue:y0+(y1-y0)*maxy];
  352. [self setMinMax:self];
  353. [d3View zoom:self];
  354. return self;
  355. }
  356.  
  357. -dumpContour
  358. {
  359. [dView saveTiff:self];
  360. return self;
  361. }
  362.  
  363. /* update the density plot */
  364. -updDen:(RtPoint)TickO :(RtPoint)TickS
  365. {
  366. static float *densData=NULL;
  367. static int ndd=0;
  368. int k,i,nx,ny;
  369. NXRect rec,rec2,tick;
  370. float x0=0,x1=0,y0=0,y1=0,zlim[4] = { -1.0,1.0,0,0};
  371. char s[80];
  372. float lev0,lev1;
  373.  
  374. zlim[2]=[minZ floatValue];
  375. zlim[3]=[maxZ floatValue];
  376.  
  377. if ([autoCont intValue]) {
  378.     i=[[levels cellAt:2 :0] intValue];
  379.     lev1=2.0/(float)i;
  380.     lev0= -1.0+lev1/2.0;
  381.     [[levels cellAt:0 :0] setFloatValue:(lev0+1.0)/2.0* 
  382.         (zlim[3]-zlim[2])+zlim[2]];
  383.     [[levels cellAt:1 :0] setFloatValue:lev1/2.0* 
  384.         (zlim[3]-zlim[2])];
  385. }
  386. else {
  387.     lev0=[[levels cellAt:0 :0] floatValue];
  388.     lev1=[[levels cellAt:1 :0] floatValue];
  389.     lev0=(lev0-zlim[2])/(zlim[3]-zlim[2])*2.0-1.0;
  390.     lev1=lev1/(zlim[3]-zlim[2])*2.0;
  391. }
  392.  
  393. if (lev1<=0) lev1=.2;
  394. [[d3View worldShape] setContour:lev0/2.0+.5 :lev1/2.0];
  395.  
  396. tick.origin.x=(TickO[0]+1.0)/2.0;
  397. tick.origin.y=(TickO[1]+1.0)/2.0;
  398. tick.size.width=TickS[0]/2.0;
  399. tick.size.height=TickS[1]/2.0;
  400.  
  401. rec2.origin.x=[minX floatValue];
  402. rec2.origin.y=[minY floatValue];
  403. rec2.size.width=[maxX floatValue]-[minX floatValue];
  404. rec2.size.height=[maxY floatValue]-[minY floatValue];
  405. rec=rec2;
  406. if (pref[curPref].ndata==0) {
  407.     [dView setData:0 :0 :NULL :zlim :tick :rec :rec2 :lev0 :lev1];
  408.     return self;
  409. }
  410.  
  411. nx=pref[curPref].nx; 
  412. ny=pref[curPref].ny;
  413. if (ndd<(nx*ny)) {
  414.     if (densData!=NULL) free(densData);
  415.     densData=malloc(nx*ny*sizeof(float));
  416.     ndd=nx*ny;
  417. }
  418.  
  419. if (pref[curPref].fileData==NULL||nx*ny==pref[curPref].ndata) {
  420.     for (k=0; k<pref[curPref].ndata; k++) densData[k]=pref[curPref].data[k].z;
  421. }
  422. else {
  423.     sprintf(s,"Error nx*ny=%d  ndata=%d\n",nx*ny,pref[curPref].ndata);
  424.     [self error:s];
  425. /*    for (k=0; k<(nx*ny); k++) densData[k]= -.5;
  426.     for (k=0; k<pref[curPref].ndata; k++) {
  427.         i=floor((pref[curPref].data[k].x+.5)*(float)nx)+ 
  428.             floor((pref[curPref].data[k].y+.5)*(float)ny)*nx;
  429.         if (i>=0 && i<ndd) densData[i]=pref[curPref].data[k].z;
  430.     }*/
  431. }
  432.  
  433. if (pref[curPref].fileData!=NULL) {
  434.     for (i=0; i<pref[curPref].nfdata; i++) {
  435.         x0=pref[curPref].fileData[i].x;
  436.         y0=pref[curPref].fileData[i].y;
  437.         if (x0>=rec2.origin.x && y0>=rec2.origin.y) break;
  438.     }
  439.     for (i=pref[curPref].nfdata-1; i>=0; i--) {
  440.         x1=pref[curPref].fileData[i].x;
  441.         y1=pref[curPref].fileData[i].y;
  442.         if (x1<=rec2.origin.x+rec2.size.width && 
  443.             y1<=rec2.origin.y+rec2.size.height) break;
  444.     }
  445.  
  446.     rec.size.width=x1-x0;
  447.     rec.origin.x=x0;
  448.     rec.size.height=y1-y0;
  449.     rec.origin.y=y0;
  450. }
  451.  
  452. if (pref[curPref].sym!= -1) [dView setData:nx :ny :densData :zlim :tick :rec :rec2 :lev0 :lev1];
  453. else [dView setData:0 :0 :NULL :zlim :tick :rec :rec2 :lev0 :lev1];
  454. return self;
  455. }
  456.  
  457. int comp(float *x,float *y)
  458. {
  459. if (x<y) return(-1);
  460. if (y>x) return(1);
  461. return (0);
  462. }
  463.  
  464. -newInsp:sender
  465. {
  466. int i;
  467.  
  468. i=[[sender selectedCell] tag];
  469. switch(i) {
  470. case 0: [I_BOX setContentView:I_lim];
  471.         break;
  472. case 1:    [I_BOX setContentView:I_dis];
  473.         break;
  474. case 2:    [I_BOX setContentView:I_pho];
  475.         break;
  476. }
  477. [I_BOX display];
  478. return self;
  479. }
  480.  
  481. /* variable slider changed */
  482. -setVarS:sender
  483. {
  484. int i;
  485.  
  486. for (i=0; i<5; i++) 
  487.     [[varText cellAt:i :0] setFloatValue:[[varSli cellAt:i :0] floatValue]];
  488. [d3View zoom:self];
  489. return self;
  490. }
  491.  
  492. /* variable text changed */
  493. -setVarT:sender
  494. {
  495. int i;
  496. for (i=0; i<5; i++) 
  497.     [[varSli cellAt:i :0] setFloatValue:[[varText cellAt:i :0] floatValue]];
  498. [d3View zoom:self];
  499. return self;
  500. }
  501.  
  502. /* new var min/max */
  503. -setVarMinMax:sender
  504. {
  505. int i;
  506.  
  507. for (i=0; i<5; i++)
  508.     [[[varSli cellAt:i :0] setMaxValue:[[varMax cellAt:i :0] floatValue]] setMinValue:[[varMin cellAt:i :0] floatValue]];
  509. return self;
  510. }
  511.  
  512. -saveData:sender
  513. {
  514. id pan;
  515. FILE *out;
  516. int i;
  517. float x0,x1,y0,y1,z0,z1;
  518. x0=[minX floatValue];
  519. x1=[maxX floatValue]-x0;
  520. y0=[minY floatValue];
  521. y1=[maxY floatValue]-y0;
  522. z0=[minZ floatValue];
  523. z1=[maxZ floatValue]-z0;
  524.  
  525. pan=[SavePanel new];
  526. if ([pan runModal]) {
  527.     out=fopen([pan filename],"w");
  528.     fprintf(out,"# Created by Plot3D\n");
  529.     for (i=0; i<pref[curPref].ndata; i++) 
  530.         fprintf(out,"%f\t%f\t%f\n",(pref[curPref].data[i].x/2.0+.5)*x1+x0,
  531.             (pref[curPref].data[i].y/2.0+.5)*y1+y0,
  532.             (pref[curPref].data[i].z/2.0+.5)*z1+z0);
  533.     fclose(out);
  534. }
  535. return self;
  536. }
  537.  
  538. /* Start rendering an animation */
  539. -makeAnim:sender
  540. {
  541. int afl=0,n=16,cf;
  542. static id savePanel=nil;
  543. float chi,chistp,theta,t,tstp;
  544. char animFile[100];        /* animation directory */
  545. char animName[30];            /* animation name */
  546. char fsp[MAXPATHLEN];
  547. int i,j=0;
  548.  
  549. if (!savePanel) {
  550.     savePanel=[SavePanel new];
  551.     [savePanel setRequiredFileType:"anim"];
  552. }
  553.  
  554. if(![savePanel runModal]) return self;
  555. if (strlen([savePanel filename])>78) {
  556.     [self error:"Filespec too long. Please notify author."];
  557.     return self;
  558. }
  559. strcpy(animFile, [savePanel filename]);
  560. mkdir(animFile,0777);
  561. for (i=strlen(animFile)-1; i>=0; i--) if (animFile[i]=='/') break;
  562. i++;
  563. while (i<strlen(animFile)-5) animName[j++]=animFile[i++];
  564. animName[j]=0;
  565. afl=[[animFlag cellAt:0 :0] intValue]*ANIM_spin+
  566.     [[animFlag cellAt:1 :0] intValue]*ANIM_t;
  567. if (afl==0) {
  568.     [self error:"Animation doesn't do anything!"];
  569.     return self;
  570. }
  571. n=[animFrames intValue];
  572. chistp=M_PI*2.0/(float)n;
  573. chi=[d3View getChi];
  574. theta=[d3View getTheta];
  575. t=[[varMin cellAt:0 :0] floatValue];
  576. tstp=([[varMax cellAt:0 :0] floatValue]-t)/(float)(n-1);
  577.  
  578. for (cf=0; cf<n; cf++) {
  579.     [[varText cellAt:0 :0] setFloatValue:t];
  580.     [[varSli cellAt:0 :0] setFloatValue:t];
  581.     [d3View setAng:theta :chi];
  582.     [d3View zoom:self];
  583.     sprintf(fsp,"%s/%s.%d.tiff",animFile,animName,cf+1);
  584.     [self message:"Rendering animation. This will take a while ..." :fsp];
  585.     [d3View renderTIFF:fsp];
  586.     if (afl&ANIM_t) t+=tstp;
  587.     if (afl&ANIM_spin) chi+=chistp;
  588. }
  589. [self message:"Rendering complete !!!" :"(use Movie.app or a similar program to view)"];
  590. return self;
  591. }
  592.  
  593. -error:(char *)msg
  594. {
  595. [errTitle setStringValue:"ERROR"];
  596. [errMsg setStringValue:msg];
  597. [errPan makeKeyAndOrderFront:self];
  598. return self;
  599. }
  600.  
  601. -message:(char *)s1 :(char *)s2
  602. {
  603. [errTitle setStringValue:s1];
  604. [errMsg setStringValue:s2];
  605. [errPan makeKeyAndOrderFront:self];
  606. return self;
  607. }
  608.  
  609. @end
  610.